package com.Activity;

import java.util.ArrayList;
import java.util.Vector;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.ZoomControls;

import com.CustomControl.MyImageView;
import com.DataStructure.Node;
import com.DataStructure.Pixel;
import com.DataStructure.Tile;
import com.Database.DBAdapter;
import com.Activity.R;

public class MapsActivity extends Activity {
	static final String TAG = "logview";
	private float mx = 0;
	private float my = 0;
	private MyImageView iv_map = null;
	private int x_default = 26014;
	private int y_default = 14425;
	private int z_default = 2;
	private ZoomControls zoom_control;
	private DBAdapter db_adapter;
	private Pixel default_poss_zoom;
	Display display;
	RelativeLayout rl_find_path;
	Button btn_find_path;
	Button btn_cancel;
	Button btn_go;
	EditText edt_start_point, edt_end_point;
	Vector<Node> nodes_to_traversal, nodes_is_traveled;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		display = getWindowManager().getDefaultDisplay();
		default_poss_zoom = new Pixel(display.getWidth() >> 1,
				display.getHeight() >> 1);
		setContentView(R.layout.main);
		rl_find_path = (RelativeLayout) findViewById(R.id.find_path);

		btn_cancel = (Button) findViewById(R.id.btn_cancel);
		btn_cancel.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				rl_find_path.setVisibility(View.INVISIBLE);
				InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
				imm.hideSoftInputFromWindow(edt_end_point.getWindowToken(), 0);
			}
		});

		btn_find_path = (Button) findViewById(R.id.btn_find_path);
		btn_find_path.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				rl_find_path.setVisibility(View.VISIBLE);
			}
		});

		edt_start_point = (EditText) findViewById(R.id.edt_startpoint);
		edt_end_point = (EditText) findViewById(R.id.edt_endpoint);

		try {
			this.iv_map = (MyImageView) findViewById(R.id.map_test);
			this.iv_map.setOnTouchListener(new View.OnTouchListener() {

				public boolean onTouch(View arg0, MotionEvent event) {

					float curX, curY;

					switch (event.getAction()) {

					case MotionEvent.ACTION_DOWN:
						mx = event.getX();
						my = event.getY();
						break;
					case MotionEvent.ACTION_MOVE:
						curX = event.getX();
						curY = event.getY();
						iv_map.scrollBy((int) (mx - curX), (int) (my - curY));
						mx = curX;
						my = curY;
						break;
					case MotionEvent.ACTION_UP:
						curX = event.getX();
						curY = event.getY();
						iv_map.scrollBy((int) (mx - curX), (int) (my - curY));
						break;
					}

					return false;
				}
			});

			iv_map.setOnLongClickListener(new OnLongClickListener() {

				@Override
				public boolean onLongClick(View arg0) {
					// TODO Auto-generated method stub
					if (rl_find_path.getVisibility() == View.VISIBLE) {
						// TODO pick nearest position
						Node curPosition = fromPixelToLatLng(
								(int) mx + iv_map.getSlidedX(), (int) my
										+ iv_map.getSlidedY(), z_default);
						Node nearest_node = getDBAdapter().getNearestNode(
								curPosition.getLat(), curPosition.getLng());
						iv_map.markerVtr.add(nearest_node);

						if (edt_start_point.isFocused()) {
							edt_start_point.setText(nearest_node.getId() + ": "
									+ nearest_node.getLat() + ","
									+ nearest_node.getLat());
							createNodesToTraversal(nearest_node);
							edt_end_point.requestFocus();

						} else if (edt_end_point.isFocused()) {
							edt_end_point.setText(nearest_node.getId() + ": "
									+ nearest_node.getLat() + ","
									+ nearest_node.getLng());
							nearest_node.setDistanceTraveled(Double.MAX_VALUE);
							insertEndNodeIntoNodesToTraversal(nearest_node);
							iv_map.requestFocus();
						}

					}
					return false;
				}
			});

			btn_go = (Button) findViewById(R.id.btn_go);
			btn_go.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View arg0) {
					createNodesIsTraveled();
					Node end_node = nodes_to_traversal
							.remove(nodes_to_traversal.size() - 1);
					while (true) {
						Node picked = null;
						if (nodes_to_traversal.isEmpty()) {
							Log.i("infomation", "FAIL");
							break;
						} else {
							picked = getNextNodeToTraversal();
						}

						if (picked.getId() == end_node.getId()) {
							Vector<Node> path = new Vector<Node>();
							while (picked != null) {
								path.add(picked);
								picked = getParent(picked.getParent());
							}
							iv_map.setPath(path);
							Log.i("infomation", "SUCCESS, path length = "
									+ path.size());
							break;
						}
						insertIntoNodesIsTraveled(picked);
						insertIntoNodesToTraversal(getDBAdapter().getNextNodes(
								picked, end_node));
					}
				}

				private Node getNextNodeToTraversal() {
					int next_node_id = 0;
					for (int i = 1, size = nodes_to_traversal.size(); i < size; i++) {
						if (nodes_to_traversal.elementAt(next_node_id)
								.getDistanceTraveled()
								+ nodes_to_traversal.elementAt(next_node_id)
										.getDistanceToEndNode() > nodes_to_traversal
								.elementAt(i).getDistanceTraveled()
								+ nodes_to_traversal.elementAt(i)
										.getDistanceToEndNode()) {
							next_node_id = i;
						}
					}
					return nodes_to_traversal.remove(next_node_id);
				}

				private Node getParent(long id) {
					for (int i = 0, size = nodes_is_traveled.size(); i < size; i++) {
						Node process = nodes_is_traveled.elementAt(i);
						if (process.getId() == id) {
							return process;
						}
					}
					return null;
				}
			});

			zoom_control = (ZoomControls) findViewById(R.id.zoom_controls);
			zoom_control.setOnZoomInClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					// TODO Auto-generated method stub
					if (z_default > 0) {
						z_default--;

						int new_center_tile_x = ((default_poss_zoom.x + iv_map
								.getSlidedX()) << 1) >> 8;
						int new_center_tile_y = ((default_poss_zoom.y + iv_map
								.getSlidedY()) << 1) >> 8;
						int delta_x = default_poss_zoom.x
								- ((default_poss_zoom.x + iv_map.getSlidedX()) << 1)
								% 256;
						int delta_y = default_poss_zoom.y
								- ((default_poss_zoom.y + iv_map.getSlidedY()) << 1)
								% 256;
						iv_map.setValue(
								getDBAdapter().getAroundPossition(
										new_center_tile_x, new_center_tile_y,
										z_default), new_center_tile_x,
								new_center_tile_y, z_default, delta_x, delta_y);
					}
				}
			});
			zoom_control.setOnZoomOutClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					// TODO Auto-generated method stub
					if (z_default < 5) {
						z_default++;

						int new_center_tile_x = ((default_poss_zoom.x + iv_map
								.getSlidedX()) >> 1) >> 8;
						int new_center_tile_y = ((default_poss_zoom.y + iv_map
								.getSlidedY()) >> 1) >> 8;
						int delta_x = default_poss_zoom.x
								- ((default_poss_zoom.x + iv_map.getSlidedX()) >> 1)
								% 256;
						int delta_y = default_poss_zoom.y
								- ((default_poss_zoom.y + iv_map.getSlidedY()) >> 1)
								% 256;
						iv_map.setValue(
								getDBAdapter().getAroundPossition(
										new_center_tile_x, new_center_tile_y,
										z_default), new_center_tile_x,
								new_center_tile_y, z_default, delta_x, delta_y);
					}
				}
			});

			try {
				iv_map.setDBAdapter(getDBAdapter());
			} catch (Exception e) {
				// TODO: handle exception
				Log.i(TAG, "Error: " + e.getMessage());
			}
			ArrayList<Tile> tiles = null;
			try {
				tiles = getDBAdapter().getAroundPossition(x_default, y_default,
						z_default);
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
				Log.i(TAG, e.toString());
			}
			iv_map.setValue(tiles, x_default, y_default, z_default,
					default_poss_zoom.x - 128, default_poss_zoom.y - 128);
		} catch (Exception e) {
			// TODO: handle exception
			Log.i(TAG, "Error: " + e.getMessage());
		}
	}

	private DBAdapter getDBAdapter() {
		if (db_adapter == null) {
			db_adapter = new DBAdapter(getApplicationContext());
		}
		return db_adapter;
	}

	public static Pixel fromLatLngToPixel(double lat, double lng, int z) {
		// longitude -> x
		// latitude -> y
		long offset = 16777216;
		double radius = offset / Math.PI;

		int z_temp = Math.abs(z);
		int x;
		if (z < 0) {
			x = (int) (offset + radius * (lng * Math.PI / 180)) << z_temp;
		} else {
			x = (int) (offset + radius * (lng * Math.PI / 180)) >> z_temp;
		}
		int y;
		if (z < 0) {
			y = (int) (offset - radius
					* Math.log((1 + Math.sin(lat * Math.PI / 180))
							/ (1 - Math.sin(lat * Math.PI / 180))) / 2) << z_temp;
		} else {
			y = (int) (offset - radius
					* Math.log((1 + Math.sin(lat * Math.PI / 180))
							/ (1 - Math.sin(lat * Math.PI / 180))) / 2) >> z_temp;
		}

		Pixel pixel = new Pixel(x, y);
		return pixel;
	}

	public static Node fromPixelToLatLng(int x, int y, int z) {
		long offset = 16777216;
		double radius = offset / Math.PI;

		double temp1;
		int z_temp = 0;
		if (z < 0) {
			z_temp = Math.abs(z);
			temp1 = ((y >> z_temp) - offset) / radius;
		} else {
			temp1 = ((y << z) - offset) / radius;
		}

		double temp2 = 2 * Math.atan(Math.exp(temp1));
		double lat = ((Math.PI / 2 - temp2) * 180) / Math.PI;

		double lng;
		if (z < 0) {
			lng = ((((x >> z_temp) - offset) / radius) * 180) / Math.PI;
		} else {
			lng = ((((x << z) - offset) / radius) * 180) / Math.PI;
		}

		Node latlng = new Node(lat, lng);
		return latlng;
	}

	private void createNodesToTraversal(Node start) {
		if (nodes_to_traversal == null) {
			nodes_to_traversal = new Vector<Node>();
		} else {
			nodes_to_traversal.removeAllElements();
		}
		nodes_to_traversal.add(start);
	}

	private void insertIntoNodesToTraversal(Vector<Node> next_nodes) {
		while (!next_nodes.isEmpty()) {
			Node process = next_nodes.remove(0);
			boolean is_continue = true;
			for (int i = 0, size = nodes_is_traveled.size(); i < size; i++) {
				if (process.getId() == nodes_is_traveled.elementAt(i).getId()) {
					is_continue = false;
					break;
				}
			}
			for (int i = 0, size = nodes_to_traversal.size(); i < size; i++) {
				if (process.getId() == nodes_to_traversal.elementAt(i).getId()) {
					is_continue = false;
					break;
				}
			}
			if (is_continue) {
				nodes_to_traversal.add(process);
			}
		}
	}

	private void insertEndNodeIntoNodesToTraversal(Node end_node) {
		nodes_to_traversal.insertElementAt(end_node, nodes_to_traversal.size());
	}

	private void createNodesIsTraveled() {
		if (nodes_is_traveled == null) {
			nodes_is_traveled = new Vector<Node>();
		} else {
			nodes_is_traveled.removeAllElements();
		}
	}

	private void insertIntoNodesIsTraveled(Node temp) {
		nodes_is_traveled.add(temp);
	}

}